package com.tanx.cqrs.infrastructure.spring.eventsourcing.handler;

import com.tanx.cqrs.aggregate.Aggregate;
import com.tanx.cqrs.eventsourcing.handler.EventSourcingHandler;
import com.tanx.cqrs.eventsourcing.handler.EventSourcingHandlerInstance;
import com.tanx.cqrs.eventsourcing.handler.EventSourcingHandlerResolver;
import org.springframework.context.ApplicationContext;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 事件溯源处理器的解析器
 */
public class SpringBeanEventSourcingHandlerResolverImpl implements EventSourcingHandlerResolver {


    private ApplicationContext context;
    private Map<Class, EventSourcingHandlerInstance> handlerMap = new ConcurrentHashMap<>();

    public SpringBeanEventSourcingHandlerResolverImpl(ApplicationContext context) {
        this.context = context;
    }

    @Override
    public void registerAllHandler() {
        Map<String, Object> beansWithAnnotation = context.getBeansWithAnnotation(Aggregate.class);
        for (Object bean : beansWithAnnotation.values()) {
            Method[] methods = bean.getClass().getMethods();
            for (Method method : methods) {
                EventSourcingHandler annotation = method.getAnnotation(EventSourcingHandler.class);
                if (annotation == null) {
                    continue;
                }
                if (method.getParameters().length != 1) {
                    throw new RuntimeException("事件溯源方法" + bean.getClass().getSimpleName() + "." + method.getName() + "参数只能有一个");
                }
                EventSourcingHandlerInstance instance = new EventSourcingHandlerInstance(bean, method);
                handlerMap.put(instance.getEventClass(), instance);
            }
        }
    }

    @Override
    public void eventSourcing(Object aggregate, Object event) {
        EventSourcingHandlerInstance instance = handlerMap.get(event.getClass());
        if (instance == null) {
            return;
        }
        try {
            instance.getMethod().invoke(aggregate, event);
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }
}
